#pragma once

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctime>
#include <cstring>
#include <cstdio>
#include <cstdarg>
#include <string>
#include <iostream>

namespace ns_log
{
    enum _output_position
    {
        CONSOLE,
        ONEFILE,
        MULTIFILE
    };

    enum _log_level
    {
        LOG,
        WARNING,
        ERROR,
        FATAL
    };

    const char* LOGFILE = "log.txt";
    const int STR_SIZE = 1024;

    class Log
    {
    public:
        Log(_output_position op_pos = CONSOLE)
            :_op_pos(op_pos)
        { }

        void operator()(_log_level log_level, const char* format, ...)
        {
            //将日志等级字符串化
            std::string level_str(log_level_to_string(log_level));
            const time_t cur_timep = (time_t)time(nullptr); //获取当前时刻的时间戳
            struct tm* cur_time = localtime(&cur_timep); //解析时间戳
            char tmp_time_str[STR_SIZE]; tmp_time_str[0]= '\0'; //将当前时间信息字符串化
            sprintf(tmp_time_str, "[%d-%d-%d] [%d:%d:%d]", 
                cur_time->tm_year + 1900, cur_time->tm_mon + 1, cur_time->tm_mday, \
                cur_time->tm_hour, cur_time->tm_min, cur_time->tm_sec);
            std::string time_str(tmp_time_str);

            //获取可变参数
            char tmp_msg[STR_SIZE]; tmp_msg[0] = '\0';
            va_list args;
            va_start(args, format);
            vsprintf(tmp_msg, format, args);
            va_end(args);
            //拼接信息
            std::string msg_str(tmp_msg);
            std::string log_msg = level_str + "->" + time_str +  ": " + tmp_msg + "\n";
            log_msg_output(log_level, log_msg);
        }

        void clear()
        {
            int fd = open("./log/log.txt", O_CREAT | O_TRUNC | O_WRONLY, 0666);
            close(fd);
            fd = open("./log/log.txt.LOG", O_CREAT | O_TRUNC | O_WRONLY, 0666);
            close(fd);
            fd = open("./log/log.txt.WARNING", O_CREAT | O_TRUNC | O_WRONLY, 0666);
            close(fd);
            fd = open("./log/log.txt.ERROR", O_CREAT | O_TRUNC | O_WRONLY, 0666);
            close(fd);
            fd = open("./log/log.txt.FATAL", O_CREAT | O_TRUNC | O_WRONLY, 0666);
            close(fd);
        }

    private:
        void log_msg_output(const _log_level log_level, const std::string& log_msg)
        {
            if(_op_pos == CONSOLE) {
                std::cout << log_msg;
            }
            else if(_op_pos == ONEFILE)
            {
                int fd = open("./log/log.txt", O_CREAT | O_APPEND | O_WRONLY, 0666);
                if(fd < 0) { perror("log_msg_output->open err"); exit(1); }
                ssize_t ret = write(fd, log_msg.c_str(), log_msg.size());
                if(ret < 0) { perror("log_msg_output->write err"); exit(1); }
                close(fd);
            }
            else if(_op_pos == MULTIFILE)
            {
                int fd = -1;
                switch(log_level)
                {
                    case LOG: fd = open("./log/log.txt.LOG", O_CREAT | O_APPEND | O_WRONLY, 0666); break;
                    case WARNING: fd = open("./log/log.txt.WARNING", O_CREAT | O_APPEND | O_WRONLY, 0666); break;
                    case ERROR: fd = open("./log/log.txt.ERROR", O_CREAT | O_APPEND | O_WRONLY, 0666); break;
                    case FATAL: fd = open("./log/log.txt.FATAL", O_CREAT | O_APPEND | O_WRONLY, 0666); break;
                }
                if(fd < 0) { perror("log_msg_output->write err"); exit(1); }
                write(fd, log_msg.c_str(), log_msg.size());
            }
            else { perror("No such level"); }
        }

        const char* log_level_to_string(_log_level log_level)
        {
            switch (log_level)
            {
                case LOG: return "LOG";
                case WARNING: return "WARNING";
                case ERROR: return "ERROR";
                case FATAL: return "FATAL";
            };
        }

    private:
        _output_position _op_pos;
    };

    Log lg(MULTIFILE);
}